/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.persistence.hibernate.core;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.common.parsers.exception.PlainTextExtractorException;
import com.blandware.atleap.common.parsers.html.HTMLPlainTextExtractor;
import com.blandware.atleap.common.util.ConvertUtil;
import com.blandware.atleap.common.util.PartialCollection;
import com.blandware.atleap.common.util.QueryInfo;
import com.blandware.atleap.model.core.BaseObject;
import com.blandware.atleap.model.core.ContentField;
import com.blandware.atleap.model.core.ContentFieldValue;
import com.blandware.atleap.model.core.ContentLocale;
import com.blandware.atleap.model.core.ContentResource;
import com.blandware.atleap.model.core.MailTemplate;
import com.blandware.atleap.model.core.Page;
import com.blandware.atleap.persistence.core.MailTemplateDAO;

import java.io.ByteArrayInputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

/**
 * <p>Hibernate implementation of MailTemplateDAO</p>
 * <p><a href="MenuDAOHibernate.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.15 $ $Date: 2008/06/23 13:24:54 $
 */
public class MailTemplateDAOHibernate extends LocalizableDAOHibernate implements MailTemplateDAO {

	/**
	 * Creates new instance of MailTemplateDAOHibernate
	 */
	public MailTemplateDAOHibernate() {
	}

	// ~ CRUD Methods ================================================================

	/**
	 * @see com.blandware.atleap.persistence.core.MailTemplateDAO#createMailTemplate(com.blandware.atleap.model.core.MailTemplate)
	 */
	public Long createMailTemplate(MailTemplate template) {
		Long templateId = (Long) getHibernateTemplate().save(template);

		// from
		ContentField fFrom = new ContentField();
		fFrom.setIdentifier("from");
		fFrom.setType(ContentField.LINE_TYPE);
		fFrom.setInternal(Boolean.TRUE);
		template.addContentField(fFrom);
		getHibernateTemplate().save(fFrom);

		// subject
		ContentField fSubject = new ContentField();
		fSubject.setIdentifier("subject");
		fSubject.setType(ContentField.LINE_TYPE);
		fSubject.setInternal(Boolean.TRUE);
		template.addContentField(fSubject);
		getHibernateTemplate().save(fSubject);

		// body
		ContentField fBody = new ContentField();
		fBody.setIdentifier("body");
		fBody.setType(ContentField.MULTILINE_TYPE);
		fBody.setInternal(Boolean.TRUE);
		template.addContentField(fBody);
		getHibernateTemplate().save(fBody);

		// charset
		ContentField fCharset = new ContentField();
		fCharset.setIdentifier("charset");
		fCharset.setType(ContentField.LINE_TYPE);
		fCharset.setInternal(Boolean.TRUE);
		template.addContentField(fCharset);
		getHibernateTemplate().save(fCharset);

        // Fill in content field values for from, subject, body and charset
        // fields for every content locale
		List contentLocales = executeFind("from ContentLocale l", true, "query.listContentLocales");
		for ( int i = 0; i < contentLocales.size(); i++ ) {
			ContentLocale contentLocale = (ContentLocale) contentLocales.get(i);
			String localeIdentifier = contentLocale.getIdentifier();

            // values for from, subject, body and charset in that locale
			String valueFrom = (String) template.getFrom().get(localeIdentifier);
			String valueSubject = (String) template.getSubject().get(localeIdentifier);
			String valueBody = (String) template.getBody().get(localeIdentifier);
			String valueCharset = (String) template.getCharset().get(localeIdentifier);

			//from
			ContentFieldValue fvFrom = new ContentFieldValue();
			fvFrom.setContentLocale(contentLocale);
			if ( valueFrom != null ) {
				fvFrom.setSimpleValue(valueFrom);
			} else {
				fvFrom.setSimpleValue("");
			}
			fvFrom.setLastUpdatedDatetime(new Date());
			fFrom.addContentFieldValue(fvFrom);
			getHibernateTemplate().save(fvFrom);

			//subject
			ContentFieldValue fvSubject = new ContentFieldValue();
			fvSubject.setContentLocale(contentLocale);
			if ( valueSubject != null ) {
				fvSubject.setSimpleValue(valueSubject);
			} else {
				fvSubject.setSimpleValue("");
			}
			fvSubject.setLastUpdatedDatetime(new Date());
			fSubject.addContentFieldValue(fvSubject);
			getHibernateTemplate().save(fvSubject);

			//body
			ContentFieldValue fvBody = new ContentFieldValue();
			fvBody.setContentLocale(contentLocale);
			if ( valueBody != null ) {
				fvBody.setValue(ConvertUtil.convertToByteArray(valueBody));
			} else {
				fvBody.setValue(new byte[0]);
			}
			fvBody.setLastUpdatedDatetime(new Date());
			fBody.addContentFieldValue(fvBody);
			getHibernateTemplate().save(fvBody);

			//charset
			ContentFieldValue fvCharset = new ContentFieldValue();
			fvCharset.setContentLocale(contentLocale);
			if ( valueCharset != null ) {
				fvCharset.setSimpleValue(valueCharset);
			} else {
				fvCharset.setSimpleValue("");
			}
			fvCharset.setLastUpdatedDatetime(new Date());
			fCharset.addContentFieldValue(fvCharset);
			getHibernateTemplate().save(fvCharset);
		}

		return templateId;
	}

	/**
	 * @see com.blandware.atleap.persistence.core.MailTemplateDAO#retrieveMailTemplate(Long)
	 */
	public MailTemplate retrieveMailTemplate(Long templateId) {
		String hql = new StringBuffer("select template, fromValue.contentLocale.identifier, fromValue.simpleValue, subject.simpleValue, charset.simpleValue, body.value ")
		        .append("from MailTemplate as template left outer join ")
		        .append("template.contentFields fromField left outer join fromField.contentFieldValues as fromValue left outer join ")
		        .append("template.contentFields subjectField left outer join subjectField.contentFieldValues as subject ")
		        .append("left outer join template.contentFields bodyField left outer join bodyField.contentFieldValues as body ")
		        .append("left outer join template.contentFields charsetField left outer join charsetField.contentFieldValues as charset ")
		        .append("where ")
		        .append("((fromValue.contentLocale = subject.contentLocale and fromValue.contentLocale = body.contentLocale and fromValue.contentLocale = charset.contentLocale) or (fromValue is null and subject is null and body is null and charset is null)) ")
		        .append("and fromField.identifier = 'from' and charsetField.identifier = 'charset' ")
		        .append("and subjectField.identifier = 'subject' and bodyField.identifier = 'body' and template.id = ?")
		        .toString();

        // Get a list of tuples (mail template, locale identifier, from value,
        // subject value, body value, charset value)
		List list = executeFind(hql, new Object[]{templateId});
        // Assign those values to corresponding templates -- get list of templates with
        // from, subject, body and charset correctly assigned
		List result = setLocalizableFields(list);
		if ( result == null || result.size() <= 0 ) {
			return null;
		} else {
			return (MailTemplate) result.get(0);
		}
	}

	/**
	 * @see com.blandware.atleap.persistence.core.MailTemplateDAO#updateMailTemplate(com.blandware.atleap.model.core.MailTemplate, java.util.Map)
	 */
	public void updateMailTemplate(MailTemplate template, Map linkedObjects) {
		getHibernateTemplate().update(template);
		HTMLPlainTextExtractor extractor = new HTMLPlainTextExtractor();
        // Update internal fields (from, subject, body and charset) of this template
		for ( int i = 0; i < template.getContentFields().size(); i++ ) {
			ContentField contentField = (ContentField) template.getContentFields().get(i);
			Map values = null;
			boolean blob = false;
			if ( "from".equalsIgnoreCase(contentField.getIdentifier()) ) {
				values = template.getFrom();
			} else if ( "subject".equalsIgnoreCase(contentField.getIdentifier()) ) {
				values = template.getSubject();
			} else if ( "body".equalsIgnoreCase(contentField.getIdentifier()) ) {
				blob = true;
				values = template.getBody();
				byte type = template.isPlain() ? ContentField.MULTILINE_TYPE : ContentField.HTML_TYPE;
				if ( contentField.getType() != type ) {
					contentField.setType(type);
					getHibernateTemplate().update(contentField);
				}
			} else if ( "charset".equalsIgnoreCase(contentField.getIdentifier()) ) {
				values = template.getCharset();
			} else {
                continue;
            }
			for ( int j = 0; j < contentField.getContentFieldValues().size(); j++ ) {
				ContentFieldValue contentFieldValue = (ContentFieldValue) contentField.getContentFieldValues().get(j);
				String localeIdentifier = contentFieldValue.getContentLocale().getIdentifier();
				String value = (String) values.get(localeIdentifier);
				if ( value != null ) {
					if ( !blob ) {
						contentFieldValue.setSimpleValue(value);
					} else {
						byte[] valueBytes = ConvertUtil.convertToByteArray(value);
						contentFieldValue.setValue(valueBytes);
						if ( linkedObjects != null ) {
							// get linked objects
							try {
								Set refs = new HashSet(extractor.extractAllRefs(new ByteArrayInputStream(valueBytes), Constants.DEFAULT_ENCODING));
								List linkedPages = new ArrayList();
								List linkedResources = new ArrayList();
								for ( Iterator k = refs.iterator(); k.hasNext(); ) {
									String ref = (String) k.next();
									BaseObject linkedObject = (BaseObject) linkedObjects.get(ref);
									if ( linkedObject != null ) {
										if ( linkedObject instanceof ContentResource ) {
											linkedResources.add(linkedObject);
										} else if ( linkedObject instanceof Page ) {
											linkedPages.add(linkedObject);
										}
									}
								}
								contentFieldValue.setLinkedResources(linkedResources);
								contentFieldValue.setLinkedPages(linkedPages);
							} catch ( PlainTextExtractorException e ) {
								// log exception
								if ( log.isErrorEnabled() ) {
									StringWriter sw = new StringWriter();
									e.printStackTrace(new PrintWriter(sw));
									log.error(sw.toString());
								}
							}
						}
					}
				} else {
					if ( !blob ) {
						contentFieldValue.setSimpleValue("");
					} else {
						contentFieldValue.setValue(new byte[0]);
					}
				}
				contentFieldValue.setLastUpdatedDatetime(new Date());
				contentFieldValue.getContentField().updateContentFieldValue(contentFieldValue);
				getHibernateTemplate().update(contentFieldValue);
			}
		}
	}

    /**
     * @see com.blandware.atleap.persistence.core.MailTemplateDAO#deleteMailTemplate(com.blandware.atleap.model.core.MailTemplate)
     */
	public void deleteMailTemplate(MailTemplate template) {
		getHibernateTemplate().delete(template);
	}

    /**
     * @see com.blandware.atleap.persistence.core.MailTemplateDAO#listMailTemplates(com.blandware.atleap.common.util.QueryInfo)
     */
	public PartialCollection listMailTemplates(QueryInfo queryInfo) {
		String whereClause = new String();
		String orderByClause = new String();
		if ( queryInfo != null ) {
			whereClause = queryInfo.getWhereClause();
			orderByClause = queryInfo.getOrderByClause();
			if ( whereClause == null || whereClause.length() == 0 ) {
				whereClause = new String();
			}
			if ( orderByClause != null && orderByClause.length() != 0 ) {
				orderByClause = " order by " + orderByClause;
			} else {
				orderByClause = new String();
			}
		}

		List list = null;
		Integer total = null;

		String localeIdentifier = null;
		if ( queryInfo != null ) {
			if ( queryInfo.getQueryParameters() != null ) {
				localeIdentifier = (String) queryInfo.getQueryParameters().get("localeIdentifier");
			}
		}

		boolean localeIdentifierPresent = localeIdentifier != null && localeIdentifier.length() > 0;

		String hqlPart = new String();
		ArrayList args = new ArrayList();
		if ( localeIdentifierPresent ) {
			args.add(localeIdentifier);
			hqlPart = new StringBuffer("from MailTemplate as template ")
			        .append("left outer join template.contentFields as fromField ")
			        .append("left outer join template.contentFields as subjectField ")
			        .append("left outer join template.contentFields as bodyField ")
			        .append("left outer join template.contentFields as charsetField ")
			        .append("left outer join fromField.contentFieldValues as fromValue ")
			        .append("left outer join subjectField.contentFieldValues as subject ")
			        .append("left outer join bodyField.contentFieldValues as body ")
			        .append("left outer join charsetField.contentFieldValues as charset ")
			        .append("where ")
			        .append("fromField.identifier = 'from' and subjectField.identifier = 'subject'")
			        .append("and bodyField.identifier = 'body' and charsetField.identifier = 'charset' ")
			        .append("and fromValue.contentLocale = subject.contentLocale and fromValue.contentLocale = body.contentLocale ")
			        .append("and fromValue.contentLocale = charset.contentLocale ")
			        .append("and fromValue.contentLocale.identifier = ? ")
			        .toString();
			if ( whereClause.length() > 0 ) {
				hqlPart += "and " + whereClause;
			}
		} else {
			hqlPart = "from MailTemplate template ";
			if ( whereClause.length() > 0 ) {
				hqlPart += "where " + whereClause;
			}
		}

		if ( queryInfo != null && (queryInfo.getLimit() != null || queryInfo.getOffset() != null) ) {
			// query count
			String hqlForTotal = "select count(distinct template.id) " + hqlPart;
			total = findUniqueIntegerResult(hqlForTotal, args.toArray());
			if ( total == null ) {
				total = new Integer(0);
			}
		}

        // If we don't have any info about the total number of results yet or
        // we know that there's something that will be found, then fetch data
		if ( total == null || total.intValue() > 0 ) {
			String hql = new String();
			if ( localeIdentifierPresent ) {
				hql = "select distinct template, fromValue.simpleValue, subject.simpleValue, charset.simpleValue " + hqlPart + orderByClause;
			} else {
				hql = "select distinct template " + hqlPart + orderByClause;
			}
			list = executeFind(hql, queryInfo, args.toArray());
			if ( total == null ) {
				total = new Integer(list.size());
			}
			if ( localeIdentifierPresent ) {
				for ( ListIterator i = list.listIterator(); i.hasNext(); ) {
					Object[] objects = (Object[]) i.next();
					MailTemplate mailTemplate = (MailTemplate) objects[0];
					mailTemplate.getFrom().put(localeIdentifier, objects[1]);
					mailTemplate.getSubject().put(localeIdentifier, objects[2]);
					mailTemplate.getCharset().put(localeIdentifier, objects[3]);
					i.set(mailTemplate);
				}
			}
		} else {
			list = new ArrayList();
		}

		return new PartialCollection(list, total);

	}

	// ~ Finders ================================================================

	/**
	 * @see com.blandware.atleap.persistence.core.MailTemplateDAO#findMailTemplateByIdentifier(String)
	 */
	public MailTemplate findMailTemplateByIdentifier(String identifier) {
		String hql = new StringBuffer("select template, fromValue.contentLocale.identifier, fromValue.simpleValue, subject.simpleValue, charset.simpleValue, body.value ")
		        .append("from MailTemplate as template left outer join ")
		        .append("template.contentFields fromField left outer join fromField.contentFieldValues as fromValue left outer join ")
		        .append("template.contentFields subjectField left outer join subjectField.contentFieldValues as subject ")
		        .append("left outer join template.contentFields bodyField left outer join bodyField.contentFieldValues as body ")
		        .append("left outer join template.contentFields charsetField left outer join charsetField.contentFieldValues as charset ")
		        .append("where ")
		        .append("((fromValue.contentLocale = subject.contentLocale and fromValue.contentLocale = body.contentLocale and fromValue.contentLocale = charset.contentLocale) or (fromValue is null and subject is null and body is null and charset is null)) ")
		        .append("and fromField.identifier = 'from' and charsetField.identifier = 'charset' ")
		        .append("and subjectField.identifier = 'subject' and bodyField.identifier = 'body' and template.identifier = ?")
		        .toString();

        // Get a list of tuples (mail template, locale identifier, from value,
        // subject value, body value, charset value)
		List list = executeFind(hql, new Object[]{identifier});
        // Assign those values to corresponding templates -- get list of templates with
        // from, subject, body and charset correctly assigned
		List result = setLocalizableFields(list);
		if ( result == null || result.size() <= 0 ) {
			return null;
		} else {
			return (MailTemplate) result.get(0);
		}
	}


	/**
	 * Finds template by ID in list of mail templates
	 *
	 * @param mailMessageTemplates list to search through
	 * @param id                   ID of template
	 * @return template
	 */
	protected MailTemplate findMailTemplateById(List mailMessageTemplates, Long id) {
		for ( int i = 0; i < mailMessageTemplates.size(); i++ ) {
			MailTemplate mailTemplate = (MailTemplate) mailMessageTemplates.get(i);
			if ( mailTemplate.getId().equals(id) ) {
				return mailTemplate;
			}
		}
		return null;
	}

	// ~ Helper methods ================================================================

	/**
	 * Collapses list by setting up values for different tamplates and locales into template's maps
	 *
	 * @param queryResult query result with 6 objects [template, locale, from, subject, body, charset]
	 * @return collapsed list
	 */
	protected List setLocalizableFields(List queryResult) {
		if ( queryResult == null || queryResult.size() <= 0 ) {
			return null;
		}

        // First make a list of templates from tuples (assigning from, subject,
        // body and charset values with specified locale)
		List mailMessageTemplates = new ArrayList(queryResult.size());
		for ( int i = 0; i < queryResult.size(); i++ ) {
			Object[] objects = (Object[]) queryResult.get(i);
            MailTemplate template = (MailTemplate) objects[0];
			String locale = (String) objects[1];
			if ( locale != null && locale.trim().length() > 0 ) {
				template.getFrom().put(locale, objects[2]);
				template.getSubject().put(locale, objects[3]);
				template.getCharset().put(locale, objects[4]);
                template.getBody().put(locale, ConvertUtil.convertToString((byte[]) objects[5]));
			}
			mailMessageTemplates.add(template);
		}

        // Now make a such list of templates that each template is encountered maximum
        // one time (if some template is encountered second time, fore example, its
        // internal fields' info is added to previous instance)
		List fullMailMessageTemplates = new ArrayList(mailMessageTemplates.size());
		for ( int i = 0; i < mailMessageTemplates.size(); i++ ) {
			MailTemplate newMailTemplate = (MailTemplate) mailMessageTemplates.get(i);
			MailTemplate existingMailTemplate = findMailTemplateById(fullMailMessageTemplates, newMailTemplate.getId());
			if ( existingMailTemplate != null ) {
				//from
				Iterator iterator = newMailTemplate.getFrom().keySet().iterator();
				while ( iterator.hasNext() ) {
					String localeIdentifier = (String) iterator.next();
					String value = (String) newMailTemplate.getFrom().get(localeIdentifier);
					if ( value == null ) {
						continue;
					}
					existingMailTemplate.getFrom().put(localeIdentifier, value);
				}

				//subject
				iterator = newMailTemplate.getSubject().keySet().iterator();
				while ( iterator.hasNext() ) {
					String localeIdentifier = (String) iterator.next();
					String value = (String) newMailTemplate.getSubject().get(localeIdentifier);
					if ( value == null ) {
						continue;
					}
					existingMailTemplate.getSubject().put(localeIdentifier, value);
				}

				//body
				iterator = newMailTemplate.getBody().keySet().iterator();
				while ( iterator.hasNext() ) {
					String localeIdentifier = (String) iterator.next();
					Object value = newMailTemplate.getBody().get(localeIdentifier);
					if ( value == null ) {
						continue;
					}
					existingMailTemplate.getBody().put(localeIdentifier, value);
				}

				//charset
				iterator = newMailTemplate.getCharset().keySet().iterator();
				while ( iterator.hasNext() ) {
					String localeIdentifier = (String) iterator.next();
					String value = (String) newMailTemplate.getCharset().get(localeIdentifier);
					if ( value == null ) {
						continue;
					}
					existingMailTemplate.getCharset().put(localeIdentifier, value);
				}

			} else {
				fullMailMessageTemplates.add(newMailTemplate);
			}
		}

		return fullMailMessageTemplates;
	}


}
